package com.molichuxing.framework.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.util.ByteArrayDataSource;

import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 邮件工具类(阿里邮箱)
 * 
 * @author zoumingyu
 * @date 2019年8月19日
 *
 */
public class EmailUtil {
	private static Logger logger = LoggerFactory.getLogger(EmailUtil.class);

	private static final String CHARSET = "UTF-8";
	private static final String CONTENT_TYPE = "text/html;charset=" + CHARSET;
	private static final String SMTP_HOST = "smtp.mxhichina.com";
	private static final String SMTP_PORT = "465";

	/**
	 * 发送简单邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTo
	 *            收件人
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			String emailTo) {
		return sendMail(emailAccount, emailPassword, subject, content, Arrays.asList(emailTo), null);
	}

	/**
	 * 发送简单邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTo
	 *            收件人
	 * @param emailCc
	 *            抄送人
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			String emailTo, String emailCc) {
		return sendMail(emailAccount, emailPassword, subject, content, Arrays.asList(emailTo), Arrays.asList(emailCc));
	}

	/**
	 * 发送简单邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTos
	 *            收件人list
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			List<String> emailTos) {
		return sendMail(emailAccount, emailPassword, subject, content, emailTos, null);
	}

	/**
	 * 发送简单邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTos
	 *            收件人list
	 * @param emailCcs
	 *            抄送人list
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			List<String> emailTos, List<String> emailCcs) {
		try {
			// 配置发送邮件的环境属性
			Properties props = getProperties(emailAccount, emailPassword);
			// 创建邮件会话
			Session mailSession = getSession(props);
			// 创建邮件消息
			Message message = getSimpleMessage(mailSession, subject, content, emailAccount, emailTos, emailCcs);
			// 发送邮件
			Transport.send(message);
		} catch (Exception e) {
			logger.error("邮件发送失败：", e);
			// TODO 发送钉钉提醒
			return false;
		}
		return true;
	}

	/**
	 * 发送带附件的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTo
	 *            收件人
	 * @param attachmentName
	 *            附件名
	 * @param is
	 *            附件输入流
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			String emailTo, String attachmentName, InputStream is) {
		return sendMail(emailAccount, emailPassword, subject, content, Arrays.asList(emailTo), null, attachmentName,
				is);
	}

	/**
	 * 发送带附件的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTo
	 *            收件人
	 * @param emailCc
	 *            抄送人
	 * @param attachmentName
	 *            附件名
	 * @param is
	 *            附件输入流
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			String emailTo, String emailCc, String attachmentName, InputStream is) {
		return sendMail(emailAccount, emailPassword, subject, content, Arrays.asList(emailTo), Arrays.asList(emailCc),
				attachmentName, is);
	}

	/**
	 * 发送带附件的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTos
	 *            收件人list
	 * @param attachmentName
	 *            附件名
	 * @param is
	 *            附件输入流
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			List<String> emailTos, String attachmentName, InputStream is) {
		return sendMail(emailAccount, emailPassword, subject, content, emailTos, null, attachmentName, is);
	}

	/**
	 * 发送带附件的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTos
	 *            收件人list
	 * @param emailCcs
	 *            抄送人list
	 * @param attachmentName
	 *            附件名
	 * @param is
	 *            附件输入流
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			List<String> emailTos, List<String> emailCcs, String attachmentName, InputStream is) {
		try {
			// 配置发送邮件的环境属性
			Properties props = getProperties(emailAccount, emailPassword);
			// 创建邮件会话
			Session mailSession = getSession(props);
			// 创建邮件消息
			Message message = getComplexMessage(mailSession, subject, content, emailAccount, emailTos, emailCcs,
					attachmentName, is);
			// 发送邮件
			Transport.send(message);
		} catch (Exception e) {
			logger.error("邮件发送失败：", e);
			// TODO 发送钉钉提醒
			return false;
		}
		return true;
	}

	/**
	 * 发送带excel的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTo
	 *            收件人
	 * @param attachmentName
	 *            附件名
	 * @param workbook
	 *            excel文件
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			String emailTo, String attachmentName, Workbook workbook) {
		return sendMail(emailAccount, emailPassword, subject, content, Arrays.asList(emailTo), null, attachmentName,
				workbook);
	}
	
	/**
	 * 发送带excel的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTo
	 *            收件人
	 * @param emailCc
	 *            抄送人
	 * @param attachmentName
	 *            附件名
	 * @param workbook
	 *            excel文件
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			String emailTo, String emailCc, String attachmentName, Workbook workbook) {
		return sendMail(emailAccount, emailPassword, subject, content, Arrays.asList(emailTo), Arrays.asList(emailCc),
				attachmentName, workbook);
	}

	/**
	 * 发送带excel的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTos
	 *            收件人list
	 * @param attachmentName
	 *            附件名
	 * @param workbook
	 *            excel文件
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			List<String> emailTos, String attachmentName, Workbook workbook) {
		return sendMail(emailAccount, emailPassword, subject, content, emailTos, null, attachmentName, workbook);
	}

	/**
	 * 发送带excel的复杂邮件
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailTos
	 *            收件人list
	 * @param emailCcs
	 *            抄送人list
	 * @param attachmentName
	 *            附件名
	 * @param workbook
	 *            excel文件
	 * @return
	 */
	public static boolean sendMail(String emailAccount, String emailPassword, String subject, String content,
			List<String> emailTos, List<String> emailCcs, String attachmentName, Workbook workbook) {
		boolean result = false;
		InputStream is = null;
		ByteArrayOutputStream out = null;
		try {
			// 临时缓冲区
			out = new ByteArrayOutputStream();
			// 创建临时文件
			workbook.write(out);
			byte[] bookByteAry = out.toByteArray();
			is = new ByteArrayInputStream(bookByteAry);
			// 发送
			result = sendMail(emailAccount, emailPassword, subject, content, emailTos, emailCcs, attachmentName, is);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				is = null;
			}
			try {
				if (out != null) {
					out.close();
				}
			} catch (IOException e) {
				out = null;
			}
		}
		return result;
	}

	/**
	 * 配置发送邮件的环境属性(Properties)
	 * 
	 * @param emailAccount
	 *            发件人账号
	 * @param emailPassword
	 *            发件人密码
	 * @return
	 */
	private static Properties getProperties(String emailAccount, String emailPassword) {
		Properties props = new Properties();
		// 表示SMTP发送邮件，需要进行身份验证
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.host", SMTP_HOST);
		// 开启ssl
		props.put("mail.smtp.ssl.enable", "true");
		// 如果使用ssl，则去掉使用25端口的配置，进行如下配置,
		props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.socketFactory.port", SMTP_PORT);
		props.put("mail.smtp.port", SMTP_PORT);
		// 使用的协议（JavaMail规范要求）
		props.setProperty("mail.transport.protocol", "smtp");
		// 发件人的账号
		props.put("mail.user", emailAccount);
		// 访问SMTP服务时需要提供的密码
		props.put("mail.password", emailPassword);

		return props;
	}

	/**
	 * 创建邮件会话(Session)
	 * 
	 * @param props
	 *            环境属性
	 * @return
	 */
	private static Session getSession(Properties props) {
		// 构建授权信息，用于进行SMTP进行身份验证
		Authenticator authenticator = new Authenticator() {
			@Override
			protected PasswordAuthentication getPasswordAuthentication() {
				// 用户名、密码
				String userName = props.getProperty("mail.user");
				String password = props.getProperty("mail.password");
				return new PasswordAuthentication(userName, password);
			}
		};
		// 使用环境属性和授权信息，创建邮件会话
		Session mailSession = Session.getInstance(props, authenticator);
		// 设置为debug模式, 可以查看详细的发送 log
		mailSession.setDebug(false);

		return mailSession;
	}

	/**
	 * 创建简单邮件消息(Message)
	 * 
	 * @param mailSession
	 *            session
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailAccount
	 *            发件人账号
	 * @param emailTos
	 *            收件人
	 * @param emailCcs
	 *            抄送人
	 * @return
	 * @throws AddressException
	 * @throws MessagingException
	 */
	private static Message getSimpleMessage(Session mailSession, String subject, String content, String emailAccount,
			List<String> emailTos, List<String> emailCcs) throws AddressException, MessagingException {
		MimeMessage message = new MimeMessage(mailSession);
		// 设置发件人
		message.setFrom(new InternetAddress(emailAccount));
		Address[] replyTo = new Address[1];
		replyTo[0] = new InternetAddress(emailAccount);
		message.setReplyTo(replyTo);
		// 设置收件人
		if (emailTos.size() > 1) {
			Address[] address = new InternetAddress[emailTos.size()];
			for (int i = 0; i < emailTos.size(); i++) {
				address[i] = new InternetAddress(emailTos.get(i));
			}
			message.setRecipients(Message.RecipientType.TO, address);
		} else {
			message.setRecipient(Message.RecipientType.TO, new InternetAddress(emailTos.get(0)));
		}
		// 设置抄送人
		if (emailCcs != null && emailCcs.size() > 0) {
			if (emailCcs.size() > 1) {
				Address[] address = new InternetAddress[emailCcs.size()];
				for (int i = 0; i < emailCcs.size(); i++) {
					address[i] = new InternetAddress(emailCcs.get(i));
				}
				message.setRecipients(Message.RecipientType.CC, address);
			} else {
				message.setRecipient(MimeMessage.RecipientType.CC, new InternetAddress(emailCcs.get(0)));
			}
		}

		// 设置邮件标题
		message.setSubject(subject);
		// 设置邮件的内容体
		message.setContent(content, CONTENT_TYPE);

		return message;
	}

	/**
	 * 创建带附件的复杂邮件消息(Message)
	 * 
	 * @param mailSession
	 *            session
	 * @param subject
	 *            邮件标题
	 * @param content
	 *            邮件内容
	 * @param emailAccount
	 *            发件人账号
	 * @param emailTos
	 *            收件人
	 * @param emailCcs
	 *            抄送人
	 * @param attachmentName
	 *            附件名
	 * @param is
	 *            附件输入流
	 * @return
	 * @throws AddressException
	 * @throws MessagingException
	 * @throws IOException
	 */
	private static Message getComplexMessage(Session mailSession, String subject, String content, String emailAccount,
			List<String> emailTos, List<String> emailCcs, String attachmentName, InputStream is)
			throws AddressException, MessagingException, IOException {
		// 解决附件名称过长格式错误的问题
		System.getProperties().setProperty("mail.mime.splitlongparameters", "false");

		MimeMessage message = new MimeMessage(mailSession);
		// 设置发件人
		message.setFrom(new InternetAddress(emailAccount, "", CHARSET));
		Address[] replyTo = new Address[1];
		replyTo[0] = new InternetAddress(emailAccount);
		message.setReplyTo(replyTo);

		// 设置收件人
		if (emailTos.size() > 1) {
			Address[] address = new InternetAddress[emailTos.size()];
			for (int i = 0; i < emailTos.size(); i++) {
				address[i] = new InternetAddress(emailTos.get(i));
			}
			message.setRecipients(Message.RecipientType.TO, address);
		} else {
			message.setRecipient(Message.RecipientType.TO, new InternetAddress(emailTos.get(0)));
		}
		// 设置抄送人
		if (emailCcs != null && emailCcs.size() > 0) {
			if (emailCcs.size() > 1) {
				Address[] address = new InternetAddress[emailCcs.size()];
				for (int i = 0; i < emailCcs.size(); i++) {
					address[i] = new InternetAddress(emailCcs.get(i));
				}
				message.setRecipients(Message.RecipientType.CC, address);
			} else {
				message.setRecipient(MimeMessage.RecipientType.CC, new InternetAddress(emailCcs.get(0)));
			}
		}
		// 设置邮件标题
		message.setSubject(subject);

		// 创建文本“节点”
		MimeBodyPart text = new MimeBodyPart();
		// 设置邮件文本的内容
		text.setContent(content, CONTENT_TYPE);

		// 创建附件“节点”
		MimeBodyPart attachment = new MimeBodyPart();
		// 文件名
		attachmentName = attachmentName.substring(attachmentName.lastIndexOf("/") + 1);
		// 读取文件
		DataHandler dh2 = new DataHandler(new ByteArrayDataSource(is, "application/png"));
		// 将附件数据添加到“节点”
		attachment.setDataHandler(dh2);
		// 设置附件的文件名（需要编码）
		attachment.setFileName(MimeUtility.encodeText(attachmentName));

		// 设置文本和 附件 的关系（合成一个大的混合“节点” / Multipart ）
		MimeMultipart mm = new MimeMultipart();
		// 如果有多个附件，可以创建多个多次添加
		mm.addBodyPart(text);
		mm.addBodyPart(attachment);
		// 混合关系
		mm.setSubType("mixed");

		// 设置整个邮件的关系（将最终的混合“节点”作为邮件的内容添加到邮件对象）
		message.setContent(mm);
		message.setSentDate(new Date());
		message.saveChanges();

		return message;
	}
}
